﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;

namespace blackhouse.WebNemo.Data.Cache {
    public class CacheDataAccess : IDataAccess {

        #region Fields

        private IDataAccess data;
        private NemoCache cache;
        private SystemNames names;

        #endregion

        #region Constructors

        public CacheDataAccess(IDataAccess data, TimeSpan cacheInterval, string connectionString)
            : this(data, new NemoCache(cacheInterval, connectionString)) { }

        internal CacheDataAccess(IDataAccess data, NemoCache cache) {
            this.data = data;
            this.cache = cache;
            this.names = new SystemNames(this.data);
        }

        #endregion

        #region Methods

        public CustomValidatorData[] GetColumnValueValidators(int columnId) {
            this.cache.Reset();
            this.cache.SetDependencyTables(0);
            this.cache.SetKey("GetColumnValueValidators");
            this.cache.SetParameters(columnId);
            CustomValidatorData[] cv = this.cache.GetData<CustomValidatorData[]>();
            if (cv == null) {
                cv = this.data.GetColumnValueValidators(columnId);
                this.cache.SetCaching(cv);
            }
            return cv;
        }

        public void SaveGroup(int id, string displayName, string identyficatorFormat, bool supportTransate, bool readOnly) {
            this.data.SaveGroup(id, displayName, identyficatorFormat, supportTransate, readOnly);
            this.cache.RefreshTime(0);
        }

        public void DeleteGroup(int id) {
            this.cache.RefreshTime(0);
            this.data.DeleteGroup(id);
        }

        public ExtensionData GetExtensions(int groupId) {
            this.cache.Reset();
            this.cache.SetDependencyTables(0);
            this.cache.SetKey("GetExtensions");
            this.cache.SetParameters(groupId);
            ExtensionData data = this.cache.GetData<ExtensionData>();
            if (data == null) {
                data = this.data.GetExtensions(groupId);
                this.cache.SetCaching(data);
            }
            return data;
        }

        public void SaveExtensions(int groupId, string groupExtension, string groupRecordExtension) {
            this.cache.RefreshTime(0);
            this.data.SaveExtensions(groupId, groupExtension, groupRecordExtension);
        }

        public GroupData[] GetAllGroups() {
            this.cache.Reset();
            this.cache.SetDependencyTables(0);
            this.cache.SetKey("GetAllGroups");
            GroupData[] d = this.cache.GetData<GroupData[]>();
            if (d == null) {
                d = this.data.GetAllGroups();
                this.cache.SetCaching(d);
            }
            this.cache.End();
            return d;
        }

        public GroupData GetGroup(int groupId) {
            return this.GetAllGroups().FirstOrDefault(g => g.Id == groupId);
        }

        public bool CheckGroupName(string checkName) {
            return data.CheckGroupName(checkName);
        }

        public DataTable GetGroupScheme(FromGroup[] groups) {
            this.cache.Reset();
            this.cache.SetDependencyTables(0);
            this.cache.SetKey("GetGroupScheme");
            this.cache.SetParameters(groups.OrderBy(fg => fg.GroupId).Select(fg => (object)fg.GroupId).ToArray());
            DataTable dt = this.cache.GetData<DataTable>();
            if (dt == null) {
                dt = this.data.GetGroupScheme(groups);
                this.cache.SetCaching(dt);
            }
            this.cache.End();
            return dt;
        }

        public GroupColumnData[] GetColumns(int groupId) {
            this.cache.Reset();
            this.cache.SetDependencyTables(0);
            this.cache.SetKey("GetColumns");
            this.cache.SetParameters(groupId);
            GroupColumnData[] dt = this.cache.GetData<GroupColumnData[]>();
            if (dt == null) {
                dt = this.data.GetColumns(groupId);
                this.cache.SetCaching(dt);
            }
            this.cache.End();
            return dt;
        }

        public GroupRecordData GetGroupRecord(SelectColumn[] selectColumns, int recordId, FromGroup[] groups, string language) {
            this.cache.Reset();
            this.cache.SetDependencyTables(groups.Select(g => g.GroupId).ToArray());
            this.cache.SetKey("GetGroupRecord");
            this.cache.SetParameters(selectColumns.Select(sc => sc.ColumnName).ToArray(), recordId, language);
            GroupRecordData d = this.cache.GetData<GroupRecordData>();
            if (d == null) {
                d = this.data.GetGroupRecord(selectColumns, recordId, groups, language);
                this.cache.SetCaching(d);
            }
            this.cache.End();
            return d;
        }

        public GroupRecordData[] GetGroupRecords(SelectColumn[] selectColumns, WhereColumn[] whereColumns, FromGroup[] groups, string language) {
            this.cache.Reset();
            this.cache.SetDependencyTables(groups.Select(g => g.GroupId).ToArray());
            this.cache.SetKey("GetGroupRecords");
            string[] setSelectColumns = selectColumns.Select(sc => sc.ColumnName).ToArray();
            this.cache.SetParameters(setSelectColumns, whereColumns.Select(wc => wc.ColumnName + ":" + wc.Where.ToString()).ToArray(), language);
            GroupRecordData[] d = this.cache.GetData<GroupRecordData[]>();
            if (d == null) {
                d = this.data.GetGroupRecords(selectColumns, whereColumns, groups, language);
                this.cache.SetCaching(d);
            }
            this.cache.End();

            // ustawimy cache dla pojedyńczych rekordów.
            this.cache.Reset();
            this.cache.SetDependencyTables(groups.Select(g => g.GroupId).ToArray());
            this.cache.SetKey("GetGroupRecord");
            foreach (GroupRecordData recData in d) {
                this.cache.SetParameters(setSelectColumns, recData.Idx, language);
                this.cache.SetCaching(recData);
            }
            this.cache.End();

            return d;
        }

        public GroupRecordData[] GetGroupRecords(SelectColumn[] selectColumns, ProcedureQuery procedure, FromGroup[] groups, string language) {
            this.cache.Reset();
            this.cache.SetDependencyTables(groups.Select(g => g.GroupId).ToArray());
            this.cache.SetKey("GetGroupRecordsProcedure");
            string[] setSelectColumns = selectColumns.Select(sc => sc.ColumnName).ToArray();
            this.cache.SetParameters(setSelectColumns, procedure.Procedure, procedure.Parameters.Select(p => p.Name + ":" + p.Value.ToString()).ToArray());
            GroupRecordData[] d = this.cache.GetData<GroupRecordData[]>();
            if (d == null) {
                d = this.data.GetGroupRecords(selectColumns, procedure, groups, language);
                this.cache.SetCaching(d);
            }
            this.cache.End();

            // ustawimy cache dla pojedyńczych rekordów.
            this.cache.Reset();
            this.cache.SetDependencyTables(groups.Select(g => g.GroupId).ToArray());
            this.cache.SetKey("GetGroupRecord");
            foreach (GroupRecordData recData in d) {
                this.cache.SetParameters(setSelectColumns, recData.Idx, language);
                this.cache.SetCaching(recData);
            }
            this.cache.End();

            return d;
        }

        //public GroupRecordData[] GetRelatedGroupRecords(SelectColumn[] selectColumns, int groupId, string columnName, int relGroupId, int relRecordId, string language, string groupType) {
        //    this.cache.Reset();
        //    this.cache.SetDependencyTables(groupId, relGroupId);
        //    this.cache.SetKey("GetRelatedGroupRecords");
        //    this.cache.SetParameters(selectColumns.Select(sc => sc.ColumnName).ToArray(), columnName, relRecordId, language);
        //    GroupRecordData[] d = this.cache.GetData<GroupRecordData[]>();
        //    if (d == null) {
        //        d = this.data.GetRelatedGroupRecords(selectColumns, groupId, columnName, relGroupId, relRecordId, language, groupType);
        //        this.cache.SetCaching(d);
        //    }
        //    this.cache.End();
        //    return d;
        //}

        public GroupRecordData[] GetRelatedGroupRecords(SelectColumn[] columns, FromGroup[] groups, WhereColumn[] whereClauses, string language) {
            this.cache.Reset();
            this.cache.SetDependencyTables(groups.Select(g => g.GroupId).ToArray());
            this.cache.SetKey("GetRelatedGroupRecords");
            this.cache.SetParameters(columns.Select(sc => sc.ColumnName).ToArray(), groups.Sum(fg => fg.LimitToIdx), language);
            GroupRecordData[] d = this.cache.GetData<GroupRecordData[]>();
            if (d == null) {
                d = this.data.GetRelatedGroupRecords(columns, groups, whereClauses, language);
                this.cache.SetCaching(d);
            }
            this.cache.End();
            return d;
        }

        public void RemoveRelationToGroupRecord(int relGroupId, int relRecordId, int parentGroupId, string parentColumnName, int parentRecordId) {
            this.cache.RefreshTime(relGroupId, parentGroupId);
            this.data.RemoveRelationToGroupRecord(relGroupId, relRecordId, parentGroupId, parentColumnName, parentRecordId);
        }

        public void AddRelationToGroupRecord(int relGroupId, int relRecordId, int parentGroupId, string parentColumnName, int parentRecordId, int userId) {
            this.cache.RefreshTime(relGroupId, parentGroupId);
            this.data.AddRelationToGroupRecord(relGroupId, relRecordId, parentGroupId, parentColumnName, parentRecordId, userId);
        }

        public void ChangeIdentyficator(int groupId, int recordId, string ident, int userId) {
            this.cache.RefreshTime(groupId);
            this.data.ChangeIdentyficator(groupId, recordId, ident, userId);
        }

        public void RemoveRecord(int groupId, int recordId, int userId) {
            this.cache.RefreshTime(groupId);
            this.data.RemoveRecord(groupId, recordId, userId);
        }

        public void SaveGroupRecord(GroupRecordData data, int groupId, int recordId, int userId) {
            this.cache.Reset();
            this.cache.SetDependencyTables(groupId);
            this.cache.SetKey("SaveGroupRecord");
            this.cache.SetParameters(recordId);
            this.data.SaveGroupRecord(data, groupId, recordId, userId);
            this.cache.RefreshTime(groupId);
            this.cache.End();
        }

        public int AddGroupRecord(GroupRecordData data, int groupId, int userId) {
            this.cache.Reset();
            this.cache.SetDependencyTables(groupId);
            this.cache.SetKey("AddGroupRecord");
            int recordId = this.data.AddGroupRecord(data, groupId, userId);
            this.cache.RefreshTime(groupId);
            return recordId;
        }

        public void SaveGroupRecordFile(int groupId, int recordId, string column, string fileName, Stream stream, long streamSize, int userId) {
            this.cache.RefreshTime(groupId);
            this.data.SaveGroupRecordFile(groupId, recordId, column, fileName, stream, streamSize, userId);
        }

        public NemoUserData[] GetNemoUsers() {
            this.cache.Reset();
            this.cache.SetDependencyTables(1);
            this.cache.SetKey("GetNemoUsers");
            NemoUserData[] d = this.cache.GetData<NemoUserData[]>();
            if (d == null) {
                d = this.data.GetNemoUsers();
                this.cache.SetCaching(d);
            }
            this.cache.End();
            return d;
        }

        public KeyValuePair<int, string>[] GetGroupSystemNames() {
            this.cache.Reset();
            this.cache.SetDependencyTables(0);
            this.cache.SetKey("GetGroupSystemNames");
            KeyValuePair<int, string>[] d = this.cache.GetData<KeyValuePair<int, string>[]>();
            if (d == null) {
                d = this.data.GetGroupSystemNames();
                this.cache.SetCaching(d);
            }
            this.cache.End();
            return d;
        }

        public TabData[] GetTabsForGroup(int groupId) {
            this.cache.Reset();
            this.cache.SetDependencyTables(0);
            this.cache.SetKey("GetTabsForGroup");
            this.cache.SetParameters(groupId);
            TabData[] data = this.cache.GetData<TabData[]>();
            if (data == null) {
                data = this.data.GetTabsForGroup(groupId);
                this.cache.SetCaching(data);
            }
            this.cache.End();
            return data;
        }

        public int CreateGroup(string systemName) {
            this.cache.RefreshTime(0);
            return this.data.CreateGroup(systemName);
        }

        public SplitColumnsData[] GetColumnsSplit(int groupId) {
            this.cache.Reset();
            this.cache.SetDependencyTables(0);
            this.cache.SetKey("GetColumnsSplit");
            this.cache.SetParameters(groupId);
            SplitColumnsData[] data = this.cache.GetData<SplitColumnsData[]>();
            if (data == null) {
                data = this.data.GetColumnsSplit(groupId);
                this.cache.SetCaching(data);
            }
            this.cache.End();
            return data;
        }

        #endregion

        public string[] GetAvalibleLanguages() {
            this.cache.Reset();
            this.cache.SetDependencyTables(0);
            this.cache.SetKey("GetAvalibleLanguages");
            string[] l = this.cache.GetData<string[]>();
            if (l == null) {
                l = this.data.GetAvalibleLanguages();
                this.cache.SetCaching(l);
            }
            return l;
        }

        public string GetTranslate(int groupId, int fieldId, int recordId, string language) {
            this.cache.Reset();
            this.cache.SetDependencyTables(groupId);
            this.cache.SetParameters(fieldId, recordId, language);
            this.cache.SetKey("GetTranslate");
            string l = this.cache.GetData<string>();
            if (string.IsNullOrEmpty(l)) {
                l = this.data.GetTranslate(groupId, fieldId, recordId, language);
                this.cache.SetCaching(l);
            }
            return l;
        }

        public string GetOryginalTranslate(int groupId, int fieldId, int recordId) {
            this.cache.Reset();
            this.cache.SetDependencyTables(groupId);
            this.cache.SetParameters(fieldId, recordId);
            this.cache.SetKey("GetOryginalTranslate");
            string l = this.cache.GetData<string>();
            if (string.IsNullOrEmpty(l)) {
                l = this.data.GetOryginalTranslate(groupId, fieldId, recordId);
                this.cache.SetCaching(l);
            }
            return l;
        }

        public void SetTranslate(int groupId, int fieldId, int recordId, string language, string value, int userId) {
            this.cache.RefreshTime(groupId);
            this.data.SetTranslate(groupId, fieldId, recordId, language, value, userId);
        }

        public string GetCustomEditControlConfiguration(int columnId) {
            this.cache.Reset();
            this.cache.SetDependencyTables(0);
            this.cache.SetParameters(columnId);
            this.cache.SetKey("GetCustomEditControlConfiguration");
            string l = this.cache.GetData<string>();
            if (string.IsNullOrEmpty(l)) {
                l = this.data.GetCustomEditControlConfiguration(columnId);
                this.cache.SetCaching(l);
            }
            return l;
        }

        public VirtualColumnData[] GetVirtualColumns(int groupId) {
            throw new NotImplementedException();
        }

        public void RemoveTranslate(int groupId, int fieldId, int recordId, string language) {
            this.cache.RefreshTime(groupId);
            this.data.RemoveTranslate(groupId, fieldId, recordId, language);
        }

        public Dictionary<int, string> GetFileGuids(string tableName, string columnName) {
            this.cache.Reset();
            this.cache.SetDependencyTables(this.names.GetGroupId(tableName));
            this.cache.SetParameters(columnName);
            this.cache.SetKey("FileGuid");
            Dictionary<int, string> guids = this.cache.GetData<Dictionary<int, string>>();
            if (guids == null) {
                guids = this.data.GetFileGuids(tableName, columnName);
                this.cache.SetCaching(guids);
            }
            return guids;
        }

        public byte[] GetFileStream(string columnName, string tableName, int recordId) {
            this.cache.Reset();
            this.cache.SetDependencyTables(this.names.GetGroupId(tableName));
            this.cache.SetParameters(columnName, recordId);
            this.cache.SetKey("FileStream");
            byte[] data = this.cache.GetData<byte[]>();
            if (data == null) {
                data = this.data.GetFileStream(columnName, tableName, recordId);
                this.cache.SetCaching(data);
            }
            return data;
        }

        public byte[] GetFileStream(string columnName, int tableId, int recordId) {
            this.cache.Reset();
            this.cache.SetDependencyTables(tableId);
            this.cache.SetParameters(columnName, recordId);
            this.cache.SetKey("FileStream");
            byte[] data = this.cache.GetData<byte[]>();
            if (data == null) {
                data = this.data.GetFileStream(columnName, tableId, recordId);
                this.cache.SetCaching(data);
            }
            return data;
        }

        public FileData GetFileInfo(string columnName, string tableName, int recordId) {
            this.cache.Reset();
            this.cache.SetDependencyTables(this.names.GetGroupId(tableName));
            this.cache.SetParameters(columnName, recordId);
            this.cache.SetKey("FileInfo");
            FileData data = this.cache.GetData<FileData>();
            if (data == null) {
                data = this.data.GetFileInfo(columnName, tableName, recordId);
                this.cache.SetCaching(data);
            }
            return data;
        }

        public FileData GetFileInfo(string columnName, int tableId, int recordId) {
            this.cache.Reset();
            this.cache.SetDependencyTables(tableId);
            this.cache.SetParameters(columnName, recordId);
            this.cache.SetKey("FileInfo");
            FileData data = this.cache.GetData<FileData>();
            if (data == null) {
                data = this.data.GetFileInfo(columnName, tableId, recordId);
                this.cache.SetCaching(data);
            }
            return data;
        }

        public bool CheckViewDeclaration(string selectDeclaration) {
            return this.data.CheckViewDeclaration(selectDeclaration);
        }

        public int CreateView(string systemName, string selectDeclaration) {
            this.cache.RefreshTime(0);
            return this.data.CreateView(systemName, selectDeclaration);
        }

    }
}
